#ifndef __MEDUSA_LINEBUFFER_H__
#define __MEDUSA_LINEBUFFER_H__

//////////////////////////////////////////////////////////////////////////

#include "Buffer.h"

NS_MEDUSA_BEGIN

class LineBuffer : public Buffer
{
    
public:
    
    static LineBuffer* New(size_t buf_size, size_t expand_size =1024) {
        return new LineBuffer(buf_size, expand_size);
    }
    /*
     static LineBuffer* New(Buffer* buf_ptr) {
     return new LineBuffer(buf_ptr);
     }
     
     static LineBuffer* New(const LineBuffer* buf_ptr) {
     return new LineBuffer(buf_ptr);
     }
     
     static LineBuffer* New(Buffer& buf) {
     return new LineBuffer(buf);
     }
     
     static LineBuffer* New(const LineBuffer& buf) {
     return new LineBuffer(buf);
     }*/
    
public:
    virtual ~LineBuffer() {
        this->free_buffer();
    }
    
protected:
    
    LineBuffer(size_t buf_size, size_t expand=1024) {
        this->buffer_ptr    = new uint8_t[buf_size];
		this->buffer_size   = buf_size;
        this->expand_size   = expand;
		this->read_offset   = 0;
		this->write_offset  = 0;
    }
    
    LineBuffer(Buffer* buf_ptr) {
        MEDUSA_ASSERT (buf_ptr!=NULL);
        
        this->Assign(buf_ptr);
    }
    
    LineBuffer(const LineBuffer* buf_ptr) {
        MEDUSA_ASSERT (buf_ptr!=NULL);
        
        this->Assign(buf_ptr);
    }
    
    LineBuffer(Buffer& buf) {
        this->Assign(&buf);
    }
    
    LineBuffer(const LineBuffer& buf) {
        this->Assign(&buf);
    }
    
    LineBuffer& operator = (Buffer* buf_ptr) {
        MEDUSA_ASSERT (buf_ptr!=NULL);
        
        this->Assign(buf_ptr);
        return *this;
    }
    
    LineBuffer& operator = (const LineBuffer* buf_ptr) {
        MEDUSA_ASSERT (buf_ptr!=NULL);
        
        this->Assign(buf_ptr);
        return *this;
    }
    
    LineBuffer& operator = (Buffer& buf) {
        this->Assign(&buf);
        return *this;
    }
    
    LineBuffer& operator = (const LineBuffer& buf) {
        this->Assign(&buf);
        return *this;
    }
    
public:
    
    void Assign(Buffer* buf_ptr) {
        MEDUSA_ASSERT (buf_ptr!=NULL);
        
        this->free_buffer();
        
        this->buffer_ptr = new uint8_t[buf_ptr->BufferSize()];
        this->buffer_size = buf_ptr->BufferSize();
        this->expand_size = buf_ptr->ExpandSize();
        this->read_offset = 0;
        this->write_offset = 0;
        while(!this->IsEmpty()) {
            this->WriteInt8(buf_ptr->ReadInt8());
        }
    }
    
    void Assign(const LineBuffer* buf_ptr) {
        MEDUSA_ASSERT (buf_ptr!=NULL);
        
        this->free_buffer();
        
        this->buffer_ptr = new uint8_t[buf_ptr->buffer_size];
        this->buffer_size = buf_ptr->buffer_size;
        this->expand_size = buf_ptr->expand_size;
        this->read_offset = buf_ptr->read_offset;
        this->write_offset = buf_ptr->write_offset;
        ::memcpy(this->buffer_ptr, buf_ptr->buffer_ptr, buf_ptr->buffer_size);
    }
    
    LineBuffer* Clone() const {
        LineBuffer* pbuf = new LineBuffer(this);
        return pbuf;
    }
    
private:
    
    void free_buffer() {
        if(this->buffer_ptr!=NULL) {
            delete []this->buffer_ptr;
            this->buffer_ptr = NULL;
            this->buffer_size = 0;
            this->read_offset = 0;
            this->write_offset = 0;
        }
    }
    
public:
    
    bool IsFull() const {
        return (this->write_offset == this->buffer_size);
    }
    
    bool IsEmpty() const {
        return (this->write_offset == 0);
    }
    
    void Clear() {
        this->read_offset = this->write_offset = 0;
    }
    
    size_t BufferSize() const {
        return this->buffer_size;
    }
    
    size_t ExpandSize() const {
        return this->expand_size;
    }
    
    size_t DataSize() const {
        return this->write_offset - this->read_offset;
    }
    
    size_t FreeSize() const {
        return this->buffer_size - this->write_offset;
    }
    
    size_t LineDataSize() const {
        return this->DataSize();
    }
    
    size_t LineFreeSize() const {
        return this->FreeSize();
    }
    
public:
    
    // Int8
    uint8_t ReadInt8() {
        uint8_t ret = 0;
		if(!this->IsEmpty()) {//this->read_offset < this->write_offset
			ret = this->buffer_ptr[this->read_offset];
			this->read_offset++;
		}
		
		return ret;
    }
    
    void WriteInt8(uint8_t val) {
        if (this->IsFull()) {//(this->write_offset + 1) > this->buffer_size
			size_t new_buff_size = this->buffer_size + this->expand_size;
			uint8_t *new_buffer = new uint8_t[new_buff_size];
            uint8_t *old_buffer = this->buffer_ptr;
            ::memcpy(new_buffer, old_buffer, this->write_offset);
			this->buffer_ptr = new_buffer;
			this->buffer_size = new_buff_size;
            delete []old_buffer;
		}
        
		this->buffer_ptr[this->write_offset] = val;
		this->write_offset++;
    }
    
    uint8_t* ReadPtr() const {
        return this->buffer_ptr+this->read_offset;
    }
    
    uint8_t* BufferPtr() const {
        return this->buffer_ptr;
    }
    
private:
    
    size_t buffer_size;
    size_t expand_size;
    size_t read_offset;
    size_t write_offset;
    uint8_t *buffer_ptr;
    
};

NS_MEDUSA_END

#endif //__MEDUSA_LINEBUFFER_H__